//ECE 454 Spring 2008 Assignment 1
//Macaranas, Joseph jjmacara
//Turner, Travis t2turner
#include "marshal.h"
#include <set>
#include <signal.h>

//main method for binder
//waits for requests from servers and clients
//processes the request (one at a time)
//sends a response based on the request
//terminates once a terminate request is received
int main (int argc, char *argv[]) {

	signal(SIGCHLD, SIG_IGN);

	//host name
	char *machine_name = (char *) malloc(255);
	//pre-defined port used for binder communication
	int port = 31337;
	//structure to store binder's connection details
	struct  sockaddr_in binder_addr;
	//structure to store connection details of a requestor
    struct  sockaddr_in node_addr;
    //socket file descriptors, sd for receives, sd2 for sends
    int     sd, sd2; //potential bug using two socket file descriptors on same port
    //address length
    int     alen;
    //block of memory used to store the data from a received message
    void 	*message;
    //used for determining size of message received
	int 	n;
	//mapping of function names to struct used to hold argTypes and server information
	map<string, server_list> func2serv;
	//set of registered servers, used for clean termination
	set<unsigned long> servs;

	//retrieve host name of the machine
	gethostname(machine_name, 255);

	//print out binder's information for clients and servers to connect to it
	cout << "BINDER_ADDRESS " << machine_name << endl;
	cout << "BINDER_PORT " << port << endl;

	//set up the binder's connection details
    memset((char *)&binder_addr, 0, sizeof(binder_addr));
    binder_addr.sin_family = AF_INET;
    binder_addr.sin_addr.s_addr = INADDR_ANY;
    binder_addr.sin_port = htons((u_short)port);

    //create a socket using UDP (SOCK_DGRAM)
    sd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
    	cerr << "Binder was unable to create a socket on port 31337." << endl;
    	exit(SOCKET_FAILURE);
    }
    //bind the binder's address to the socket

    n = bind(sd, (struct sockaddr *)&binder_addr, sizeof(binder_addr));
    if (n < 0) {
         cerr << "Binder was unable to bind to the socket. Reason: " << strerror(errno) << endl;
         exit(SOCKET_FAILURE);
    }
	//set the address length
    alen = sizeof(struct sockaddr);

    //main binder loop - accept and handle requests
    //termination condition is inside, uses break
    while (1) {
		//struct used to interpret the received request
    	binder_msg *received;
    	//allocate memory to receive request
    	message = (void *) malloc(MAX_LENGTH);
    	//struct used to send a reply or acknowledge via requestType in binder_msg
    	binder_msg *reply = (binder_msg *)malloc(MAX_LENGTH);
    	//indicator used for acknowledgement reply
    	reply->requestType = REQUEST_ERROR;
    	//the marshalled message that will be sent back to the client as a reply
   		void* marshalled;

		try {

	       	//blocking receive from any client or server that uses the socket
    	   	if ((n = recvfrom(sd, message, (size_t) 31337, 0, (struct sockaddr *)&node_addr, (socklen_t *)&alen)) < 0) {
	    	   	cerr << "Failed to receive a message during a transmission attempt." << endl;
	    	   	free(message);
	    	   	free(reply);
				throw RECV_ERROR;
			}
			if (n >= 0) {
				cout << "Binder received a request. Processing request..." << endl;
			}
			//demarshall the request to interpret the details
   			received = (binder_msg *)malloc(n);
   			binderUnpackData(message, received);
   			free(message);
			cout << "Binder processing a request with type " << received->requestType << "." << endl;
		
			//the request is a register request from a server
   	   		if (received->requestType == SERVER_REGISTER) {
   	   			//check to see if there is already a server registered for that function name
   	   			map<string, server_list>::iterator temp = func2serv.find((string) received->name);
				//insert into set of servers registered
				//if server already registered, insert will not occur due to set's functionality
				servs.insert(node_addr.sin_addr.s_addr);
   	   			//a function with the name already exists in the map
   	   			if (temp != func2serv.end()) {
   	   				cout << "Server registering a function with the same name as another ";
   	   				cout << "previously registered function by this or another server." << endl;
   	   				//flag used to find node in linked list with arguments matching registering function
   	   				bool found = false;
   	   				//pointer to navigate the linked list in the mapping
   	   				server_list *current;
   	   				current = &temp->second;
   	   				//find node in linked list with matching argTypes
   	   				while (current != 0) {
   	   					//flag indicating if all the arguments match
   	   					bool argsMatch = true;
   	   					//check each argument type for the registering function compared to the current node in linked list
   	   					int *x = current->argTypes;
   	   					int *y = received->argTypes;
   	   					//ensure lengths of arrays match
   	   					//ensure each element in array matches
   	   					while (1) {
   	   						//array length doesn't match
   	   						if (*x == 0 && *y != 0) {
   	   							argsMatch = false;
   	   							break;
   	   						//array length doesn't match
   	   						} else if (*y == 0 && *x != 0) {
   	   							argsMatch = false;
   	   							break;
   	   						//end of both arrays, true match
   	   						} else if (*x == 0 && *y == 0) {
   	   							break;
   	   						//current element in both arrays match
   	   						//check next element
   	   						} else {
   	   							x++;
   	   							y++;
   	   						}
   	   					}
   	   					//all arguments matched, place to add registering server in mapping has been found
   	   					if (argsMatch) {
   	   						found = true;
   	   						break;
   	   					}
						//check next node on the linked list
   	   					current = current->next;
   	   				}
					//found place to insert the registering server in the mapping
   	   				//ensure that the server isn't already in the mapping though
   	   				if (found) {
   	   					cout << "Server registering a function with the same name and argument";
   	   					cout << " types as a previously registered function by this or another";
   	   					cout << " server." << endl;
   	   					//flag indicating whether the registering server should be added to mapping
   	   					//or if it is already in mapping
   	   					bool dontAdd = false;
   	   					//used to keep track of how many servers and functions are in array
   	   					int sizeServs = 0;
   	   					int sizeFuncs = 0;
   	   					//used for traversing the servers array
   	   					unsigned long *tUL;
   	   					tUL = current->servers;
   	   					//check each element in servers array and compare with registering server
   	   					while (*tUL != 0) {
   	   						//registering server is in the array
   	   						if (*tUL == node_addr.sin_addr.s_addr) {
   	   							//do not add server to array
   	   							cout << "Server has already registered this function. Request ignored." << endl;
   	   							dontAdd = true;
   	   							reply->requestType = REQUEST_WARNING;
   	   					   		reply->name = (char *)malloc(sizeof(char) * 4);
   	   							reply->name[0] = '\0';
   	   							reply->argTypes = (int *)malloc(sizeof(int));
   	   							reply->argTypes[0] = 0;
   	   							break;
   	   						}
   	   						//keep track of size of array
   	   						sizeServs += sizeof(unsigned long);
   	   						tUL++;
   	   					}
   	   					//include the 0 at end of servers array
   	   					sizeServs += sizeof(unsigned long);
   	   					//add server to end of array
   	   					if (!dontAdd) {
   	   						//reallocate space to make room for new entry in array
   	   						current->servers = (unsigned long *)realloc(current->servers, 
   	   												sizeServs + sizeof(unsigned long));
   	   						//find end of array in reallocated space
   	   						tUL = current->servers;
   	   						while (*tUL != 0) {
   	   							tUL++;
   	   						}
   	   						*tUL = node_addr.sin_addr.s_addr;
   	   						tUL++;
   	   						//end the servers array with a 0
   	   						*tUL = 0;
   	   						//find end of concatenated block of functions and calculate array size
   	   						function *fF;
   	   						fF = current->functions;
   	   						while (*fF != 0) {
   	   							sizeFuncs += sizeof(function);
   	   							fF++;
   	   						}
   	   						//include the 0 at end of functions array
   	   						sizeFuncs += sizeof(function);
   	   						//reallocate space to make room for new entry in functions array
   	   						current->functions = (function *)realloc(current->functions, 
   	   												sizeFuncs + sizeof(function));
   	   						//find end of array in reallocated space
   	   						fF = current->functions;
   	   						while (*fF != 0) {
   	   							fF++;
   	   						}
   	   						//a registering request would only have a non-zero element at index 0
   	   						//in received->functions
   	   						*fF = received->functions[0];
   	   						fF++;
   	   						//end the functions array with a 0
   	   						*fF = 0;
   	   						reply->requestType = REQUEST_SUCCESS;
   	   						reply->name = (char *)malloc(sizeof(char) * 4);
   	   						reply->name[0] = '\0';
   	   						reply->argTypes = (int *)malloc(sizeof(int));
   	   						reply->argTypes[0] = 0;
   	   						cout << "Server's function registration successful." << endl;
   	   					}
   	   				//did not find place to insert registering server in the linked list
   	   				//add registering server to end of linked list for the function name
   	   				} else {
   	   					cout << "Server registering a new set of argTypes for this function name." << endl;
   	   					//used for traversing to the end of the linked list
   	   					server_list *sl;
   	   					sl = &temp->second;
   	   					//navigate to the end of the linked list
   	   					while (sl->next != NULL) {
   	   						sl = sl->next;
   	   					}
   	   					//add a new node to the end of the linked list with only the
   	   					//registering server's information for now
   	   					server_list new_list;
	   	   				new_list.argTypes = received->argTypes;
	   	   				//initialize the servers array, pad end with a 0
   	   					unsigned long *tUL = (unsigned long *) malloc(sizeof(unsigned long) * 2);
   	   					unsigned long *tUL2;
   	   					*tUL = node_addr.sin_addr.s_addr;
   	   					tUL2 = tUL;
   	   					tUL2++;
   	   					*tUL2 = 0;
						//initialize the functions array, pad end with a 0
   	   					function *fF = (function *) malloc(sizeof(function) * 2);
   	   					function *fF2;
   	   					*fF = received->functions[0];
   	   					fF2 = fF;
   	   					fF2++;
   	   					*fF2 = 0;
   	   					new_list.servers = tUL;
   	   					new_list.functions = fF;
   	   					new_list.next = 0;
   	   					//finish adding to end of linked list
   	   					sl->next = &new_list;
   	   					reply->requestType = REQUEST_SUCCESS;
   	   					reply->name = (char *)malloc(sizeof(char) * 4);
   	   					reply->name[0] = '\0';
   	   					reply->argTypes = (int *)malloc(sizeof(int));
   	   					reply->argTypes[0] = 0;
   	   					cout << "Server's function registration successful." << endl;
   	   				}
				//nothing currently in map for the function name
   	   			} else {
   	   				   	   				
					//add a new node for the new linked list that the function name will map to
   	   				server_list new_list;
					//copy the argTypes into the node
	   	   			new_list.argTypes = received->argTypes;
					//initialize the servers array, pad end with a 0
   	   				unsigned long *tUL = (unsigned long *) malloc(sizeof(unsigned long) * 2);
   	   				unsigned long *tUL2;
   	   				*tUL = node_addr.sin_addr.s_addr;
   	   				tUL2 = tUL;
   	   				tUL2++;
   	   				*tUL2 = 0;
   	   				//initialize the functions array, pad end with a 0
   	   				function *fF = (function *) malloc(sizeof(function) * 2);
   	   				function *fF2;
   	   				*fF = received->functions[0];
   	   				fF2 = fF;
   	   				fF2++;
   	   				*fF2 = 0;
   	   				new_list.servers = tUL;
   	   				new_list.functions = fF;
   	   				new_list.next = 0;
					//finish adding to end of linked list
					func2serv.insert( pair<string, server_list>((string) received->name, new_list) );
   	   				reply->requestType = REQUEST_SUCCESS;
   	   				reply->name = (char *) malloc(sizeof(char) * 4);
   	   				reply->name[0] = '\0';
   	   				reply->argTypes = (int *) malloc(sizeof(int));
   	   				reply->argTypes[0] = 0;
   	   				cout << "Server's function registration successful." << endl;
   	   			} //end if-else

   	   		//the request is a deregister request from a server
   	   		//the degister algorithm is inefficient due to linked lists and linear searches
   	   		} else if (received->requestType == SERVER_DEREGISTER) {
				cout << "Binder processing a de-register request by a server." << endl;
   	   			//remove the server from the set
   	   			servs.erase(node_addr.sin_addr.s_addr);
   	   			//iterate through the map's elements specified by name
   	   			//and remove all references to the server's functions  				//use strtok to get all the relevant function names
  				//implemented by the server
  				char *aName;
  				aName = strtok(received->name, "(");
  				while (aName != NULL) {
  					map<string, server_list>::iterator it = func2serv.find((string) aName);
  					if (it != func2serv.end()) {    					//go through the linked list and remove references to the server's functions, if found
    					server_list *current;
    					current = &it->second;
    					while (current != 0) {
    						//check the array of servers and functions for references
    						//rearrange arrays if reference is found
    						unsigned long *checker1;
    						checker1 = current->servers;
    						function *func1;
    						func1 = current->functions;
    						//traverse until end of array
    						while (*checker1 != 0) {
    							//reference found, rearrange arrays by removing the reference
    							//and shifting the remaining elements in array left
    							//0 at the end will ensure proper end of array
    							if (*checker1 == node_addr.sin_addr.s_addr) {
    								unsigned long *checker2 = checker1;
    								checker2++;
    								function *func2 = func1;
    								func2++;
    								while (*checker2 != 0) {
    									*checker1 = *checker2;
    									*func1 = *func2;
										checker1++;
										checker2++;
										func1++;
										func2++;
    								}
    								//pad end of array with 0 that wasn't added due to while
    								*checker1 = 0;
    								*func1 = 0;
    								break;
    							} else {
    								checker1++;
    								func1++;
    							}
    						}
    						//remove node from linked list if server
    						//was the only server registered for that
    						//linked list node
    						if (*(current->servers) == 0) {
    							//see if linked list node was head or another node
    							map<string, server_list>::iterator itr;
    							itr = func2serv.find((string) aName);
    							server_list *finder;
    							finder = &itr->second;
    							//node was at head of linked list
    							//could have been only node in linked list
    							//remove entry from map if this is the case
    							//only remove node otherwise
    							if (finder == current) {
    								//only node in linked list, remove from map
    								if (current->next == 0) {
    									func2serv.erase(it);
    								//change the head of linked list
    								} else {
    									func2serv.erase(it);
    									server_list *replacement = &it->second;
    									*replacement = *current->next;
    								}
    							} else { 
    								itr++;
    								//node not at head of linked list, remove node
    								//and fix the "next" pointers
    								//keep track of previously checked node
    								server_list *previous = finder;
    								while ( itr != func2serv.end() ) {
    									finder = &itr->second;
    									if (finder == current) {
    										previous->next = current->next;
    										break;
    									}
    									itr++;
    								}
    							}
    						}
    						//check next linked list node
    						//possible that a server implements
    						//a function with the same name 
    						//multiple times but with different argTypes
    						current = current->next;
    					}
   	   				}
   	   				aName = strtok(NULL, "(");
  				}
   	   			//set up ack
   	   			reply->requestType = REQUEST_SUCCESS;
   	   			reply->name = (char *)malloc(sizeof(char) * 4);
   	   			reply->name[0] = '\0';
   	   			reply->argTypes = (int *)malloc(sizeof(int));
   	   			reply->argTypes[0] = 0;
   	   			cout << "Binder successfully de-registered the server's functions." << endl;

			//the request is a terminate request from a client
   	   		} else if (received->requestType == TERMINATOR) {
   	   			cout << "Binder received system termination request." << endl;
   	   			//iterate through all the servers in the set and send terminate message
  				set<unsigned long>::iterator itr;
   	   			for (itr = servs.begin(); itr != servs.end(); itr++) {
   	   				//prepare message to send to server
   	   				void *marshalled = (void *) malloc(10 * sizeof(int));
   	   				int msg_length = 10 * sizeof(int);
   	   				int *temp_msg = (int *) marshalled;
   	   				for (int a = 0; a < 10; a++) {
   	   					*temp_msg = 0;
   	   					temp_msg++;
   	   				}
   	   				//set up connection to server
	   				struct  sockaddr_in server_address;
	   				int     sds;
					int     ports;
					int     ns;
					server_address.sin_family = AF_INET;
					ports = PORT;
					server_address.sin_port = htons((u_short)ports);
					server_address.sin_addr.s_addr = *itr;
					sds = socket(PF_INET, SOCK_DGRAM, 0);
					if (sds < 0) {
						cerr << "Socket creation to tell server to terminate failed." << endl;
					}
					//send marshalled message to server
					ns = sendto(sds, marshalled, msg_length, 0, (struct sockaddr *)&server_address, sizeof(struct sockaddr) );
					if (ns == -1) {
						cerr << "Unable to transmit data to the server." << endl;
					}
					free(marshalled);
					close(sds);
   	   			} //end for

   	   			//clean up the map and set
   	   			func2serv.clear();
   	   			servs.clear();
   	   			cout << "Binder has completed termination of all servers." << endl;
   	   			cout << "Binder now exiting." << endl;
   	   			//terminate loop to exit
   	   			break;

   	   		//the request is a server-lookup request from a client
   	   		} else if (received->requestType == CLIENT_LOOKUP) {
   	   			cout << "Binder performing a server lookup for a client." << endl;
   		   		//match the function name and arg types to a server address
	 			map<string, server_list>::iterator temp = func2serv.find((string) received->name);
   		   		//found at least one server with matching name, compare argtypes
   	   			if (temp != func2serv.end()) {
   	   				//used to traverse through the linked list for the function name
   	   				server_list *curr_list;
   	   				curr_list = &temp->second;
   	   				//flag indicating if there exists a node in the linked list with matching argTypes
   	   				bool match_made = false;
   	   				//traverse through the linked list
	   	   			while (curr_list != NULL) {
						//flag indicating if argTypes match with client's lookup request
   	   					bool args_match = true;
   	   					//check each argType to see if it's a match
   	   					for (int x = 0; curr_list->argTypes[x] != 0; x++) {
   	   						//args for current array index don't match
   	   						if (curr_list->argTypes[x] != received->argTypes[x]) {
   	   							args_match = false;
   	   							break;
   	   						}
   	   					}
   	   					//check to see that servers array wasn't erased from a deregister
   	   					if (*curr_list->servers == 0) {
   	   						args_match = false;
   	   						break;
   	   					}
   	   					//all arguments matched, set up reply to be sent
   	   					if (args_match) {
   	   						reply->requestType = REQUEST_SUCCESS;
   	   						reply->name = (char *)malloc(sizeof(char) * 4);
   	   						reply->name[0] = '\0';
   	   						reply->argTypes = (int *)malloc(sizeof(int));
   	   						reply->argTypes[0] = 0;
   	   						reply->servers = curr_list->servers;
   	   						reply->functions = curr_list->functions;
   	   						match_made = true;
   	   						cout << "Binder found servers that registered the procedure." << endl;
   	   						break;
   	   					//not all arguments matched, check next node in linked list
   	   					} else {
   	   						curr_list = curr_list->next;
   	   					}
   	   				} //end while
   	   				
   	   				if (!match_made) {
   	   					reply->requestType = REQUEST_ERROR;
   	   				   	reply->name = (char *)malloc(sizeof(char) * 4);
   	   					reply->name[0] = '\0';
   	   					reply->argTypes = (int *)malloc(sizeof(int));
   	   					reply->argTypes[0] = 0;
   	   					cout << "Binder found no servers that registered the procedure." << endl;
   	   				}
   	   				
   	   			} else {
   	   				//no server matching the function signature found
   	   				//would leave reply->requestType = REQUEST_ERROR
   	   				cout << "Binder found no servers that registered the procedure." << endl;
   	   				reply->requestType = REQUEST_ERROR;
   	   				reply->name = (char *)malloc(sizeof(char) * 4);
   	   				reply->name[0] = '\0';
   	   				reply->argTypes = (int *)malloc(sizeof(int));
   	   				reply->argTypes[0] = 0;
   	   			}

   	   		} //end if-else for request type

   	   		//free up the memory used by the message received
   	   		if (received->servers != NULL) {
   	   			free(received->servers);
   	   		}
   	   		if (received->functions != NULL) {
   	   			free(received->functions);
   	   		}
			free(received);
   	   		//send back acknowledgement or reply
   	   		cout << "Binder creating reply or acknowledgement message." << endl;
   	   		//marshall the reply message into a void *
   	   		int replyLength = -1;
   	   		marshalled = malloc(MAX_LENGTH);
 	  	   	binderPackData(marshalled, reply, &replyLength);
   		   	marshalled = realloc(marshalled, replyLength);
   		   	//set up the connection for the reply/acknowledgement
			node_addr.sin_port = htons((u_short)port+1);
			sd2 = socket(PF_INET, SOCK_DGRAM, 0);
			if (sd2 < 0){
       			cerr << "Socket creation failed for responding to a binder request." << endl;
     		}
     		cout << "Binder transmitting reply." << endl;
			n = sendto(sd2, marshalled, MAX_LENGTH, 0, (struct sockaddr *)&node_addr, sizeof(struct sockaddr));
   			if (n == -1) {
     			cerr << "Error sending the response to a binder request." << endl;
   			} else {
   				cout << "Binder successfully processed a request." << endl;
   			}

	   		//free the memory used for sending data
	   		free(marshalled);
	   		free(reply->name);
	   		free(reply->argTypes);
	   		free(reply);
	   	
	   		//close the socket file descriptor used for sending reply
   			close(sd2);

		//recvfrom exception, go to next iteration of loop
		} catch ( int ex ) {
		}
		
		//add some whitespace to output
		cout << endl;

    } //end while

    //close the socket descriptor
    close(sd);

} //end main

